"""
增强的设备监控模块
实现快速离线检测：2-5秒内多次ping失败或ONVIF接口访问失败时，立即标记设备为离线
"""

import asyncio
import time
import socket
from datetime import datetime, timedelta
from typing import Optional, List, Dict, Tuple
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor, as_completed
import requests
from requests.auth import HTTPDigestAuth
import ping3
from loguru import logger

from ..models.device import DeviceInfo, DeviceStatus, NetworkMetrics, DeviceStatusRecord
from ..utils.config import Config


@dataclass
class QuickCheckResult:
    """快速检测结果"""
    ip_address: str
    ping_success: bool
    ping_latency: Optional[float]  # 毫秒
    onvif_success: bool
    onvif_response_time: Optional[float]  # 毫秒
    error_message: str = ""
    timestamp: datetime = None
    
    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = datetime.now()
    
    @property
    def is_online(self) -> bool:
        """判断设备是否在线"""
        return self.ping_success and self.onvif_success


@dataclass
class FailureTracker:
    """失败跟踪器"""
    device_id: str
    ip_address: str
    consecutive_failures: int = 0
    first_failure_time: Optional[datetime] = None
    last_check_time: Optional[datetime] = None
    failure_reasons: List[str] = None
    
    def __post_init__(self):
        if self.failure_reasons is None:
            self.failure_reasons = []
    
    def record_failure(self, reason: str):
        """记录失败"""
        now = datetime.now()
        
        if self.consecutive_failures == 0:
            self.first_failure_time = now
        
        self.consecutive_failures += 1
        self.last_check_time = now
        self.failure_reasons.append(f"{now.strftime('%H:%M:%S')}: {reason}")
        
        # 只保留最近10条失败记录
        if len(self.failure_reasons) > 10:
            self.failure_reasons = self.failure_reasons[-10:]
    
    def record_success(self):
        """记录成功"""
        self.consecutive_failures = 0
        self.first_failure_time = None
        self.last_check_time = datetime.now()
        self.failure_reasons.clear()
    
    @property
    def failure_duration(self) -> Optional[float]:
        """失败持续时间（秒）"""
        if self.first_failure_time is None:
            return None
        return (datetime.now() - self.first_failure_time).total_seconds()


class EnhancedDeviceMonitor:
    """增强的设备监控器"""
    
    def __init__(self, config: Config):
        self.config = config
        self.failure_trackers: Dict[str, FailureTracker] = {}
        
        # 快速检测配置
        self.quick_check_timeout = 2.0  # 2秒超时
        self.quick_check_interval = 1.0  # 1秒间隔
        self.max_quick_checks = 5  # 最多5次快速检测
        self.offline_threshold_seconds = 5.0  # 5秒内连续失败则标记离线
        
        # ONVIF检测配置
        self.onvif_timeout = 3.0  # ONVIF接口3秒超时
        self.onvif_endpoints = [
            "/onvif/device_service",
            "/onvif/Device",
            "/Device"
        ]
        
        # 线程池用于并发检测
        self.executor = ThreadPoolExecutor(max_workers=10)
        
        logger.info("增强设备监控器初始化完成")
    
    def quick_ping(self, ip_address: str, timeout: float = None) -> Tuple[bool, Optional[float]]:
        """
        快速ping检测
        
        Args:
            ip_address: 目标IP地址
            timeout: 超时时间（秒）
            
        Returns:
            Tuple[bool, Optional[float]]: (是否成功, 延迟毫秒)
        """
        timeout = timeout or self.quick_check_timeout
        
        try:
            response_time = ping3.ping(ip_address, timeout=timeout)
            if response_time is not None:
                return True, response_time * 1000  # 转换为毫秒
            else:
                return False, None
        except Exception as e:
            logger.debug(f"快速ping {ip_address} 失败: {e}")
            return False, None
    
    def quick_onvif_check(self, ip_address: str, port: int = 80, timeout: float = None) -> Tuple[bool, Optional[float]]:
        """
        快速ONVIF接口检测
        
        Args:
            ip_address: 目标IP地址
            port: 端口号
            timeout: 超时时间（秒）
            
        Returns:
            Tuple[bool, Optional[float]]: (是否成功, 响应时间毫秒)
        """
        timeout = timeout or self.onvif_timeout
        
        for endpoint in self.onvif_endpoints:
            url = f"http://{ip_address}:{port}{endpoint}"
            
            try:
                start_time = time.time()
                response = requests.get(
                    url,
                    timeout=timeout,
                    headers={'User-Agent': 'ONVIF-Monitor/1.0'}
                )
                response_time = (time.time() - start_time) * 1000  # 转换为毫秒
                
                # 检查响应状态
                if response.status_code in [200, 400, 401, 405]:  # 这些状态码表示服务存在
                    logger.debug(f"ONVIF检测成功: {url} - {response.status_code} - {response_time:.1f}ms")
                    return True, response_time
                    
            except requests.exceptions.Timeout:
                logger.debug(f"ONVIF检测超时: {url}")
                continue
            except requests.exceptions.ConnectionError:
                logger.debug(f"ONVIF连接失败: {url}")
                continue
            except Exception as e:
                logger.debug(f"ONVIF检测异常: {url} - {e}")
                continue
        
        return False, None
    
    def perform_quick_check(self, ip_address: str, port: int = 80) -> QuickCheckResult:
        """
        执行快速检测
        
        Args:
            ip_address: 目标IP地址
            port: ONVIF端口
            
        Returns:
            QuickCheckResult: 检测结果
        """
        logger.debug(f"开始快速检测: {ip_address}:{port}")
        
        # 并发执行ping和ONVIF检测
        ping_future = self.executor.submit(self.quick_ping, ip_address)
        onvif_future = self.executor.submit(self.quick_onvif_check, ip_address, port)
        
        try:
            # 等待结果
            ping_success, ping_latency = ping_future.result(timeout=self.quick_check_timeout + 1)
            onvif_success, onvif_response_time = onvif_future.result(timeout=self.onvif_timeout + 1)
            
            result = QuickCheckResult(
                ip_address=ip_address,
                ping_success=ping_success,
                ping_latency=ping_latency,
                onvif_success=onvif_success,
                onvif_response_time=onvif_response_time
            )
            
            logger.debug(f"快速检测完成: {ip_address} - ping: {ping_success}, onvif: {onvif_success}")
            return result
            
        except Exception as e:
            logger.warning(f"快速检测异常: {ip_address} - {e}")
            return QuickCheckResult(
                ip_address=ip_address,
                ping_success=False,
                ping_latency=None,
                onvif_success=False,
                onvif_response_time=None,
                error_message=str(e)
            )
    
    def rapid_offline_detection(self, device: DeviceInfo) -> Tuple[bool, str]:
        """
        快速离线检测：2-5秒内多次检测失败则标记为离线
        
        Args:
            device: 设备信息
            
        Returns:
            Tuple[bool, str]: (是否离线, 失败原因)
        """
        device_id = device.device_id
        ip_address = device.ip_address
        
        logger.info(f"开始快速离线检测: {ip_address} ({device_id})")
        
        # 获取或创建失败跟踪器
        if device_id not in self.failure_trackers:
            self.failure_trackers[device_id] = FailureTracker(device_id, ip_address)
        
        tracker = self.failure_trackers[device_id]
        
        # 执行多次快速检测
        check_count = 0
        failure_count = 0
        failure_reasons = []
        
        start_time = time.time()
        
        while check_count < self.max_quick_checks and (time.time() - start_time) < self.offline_threshold_seconds:
            check_result = self.perform_quick_check(ip_address, device.port)
            check_count += 1
            
            if not check_result.is_online:
                failure_count += 1
                
                # 记录具体失败原因
                reasons = []
                if not check_result.ping_success:
                    reasons.append("ping失败")
                if not check_result.onvif_success:
                    reasons.append("ONVIF接口无响应")
                if check_result.error_message:
                    reasons.append(f"错误: {check_result.error_message}")
                
                failure_reason = f"第{check_count}次检测: " + ", ".join(reasons)
                failure_reasons.append(failure_reason)
                tracker.record_failure(failure_reason)
                
                logger.debug(f"检测失败 {failure_count}/{check_count}: {ip_address} - {failure_reason}")
            else:
                # 检测成功，重置失败计数
                logger.debug(f"检测成功: {ip_address} - ping: {check_result.ping_latency:.1f}ms, onvif: {check_result.onvif_response_time:.1f}ms")
                tracker.record_success()
                break
            
            # 短暂等待后进行下一次检测
            if check_count < self.max_quick_checks:
                time.sleep(self.quick_check_interval)
        
        # 判断是否应该标记为离线
        elapsed_time = time.time() - start_time

        # 如果有成功的检测，则认为设备在线
        if failure_count < check_count:
            logger.info(f"设备快速检测通过: {ip_address} - {check_count - failure_count}/{check_count} 次成功")
            return False, ""

        # 如果所有检测都失败，则标记为离线
        if failure_count >= 3 or (failure_count >= 2 and elapsed_time <= self.offline_threshold_seconds):
            failure_summary = f"在{elapsed_time:.1f}秒内连续{failure_count}次检测失败: " + "; ".join(failure_reasons)
            logger.warning(f"设备快速离线检测: {ip_address} - {failure_summary}")
            return True, failure_summary

        # 边界情况：检测次数不足但都失败
        failure_summary = f"连续{failure_count}次检测失败，耗时{elapsed_time:.1f}秒"
        logger.warning(f"设备可能离线: {ip_address} - {failure_summary}")
        return True, failure_summary
    
    def get_failure_tracker(self, device_id: str) -> Optional[FailureTracker]:
        """获取设备的失败跟踪器"""
        return self.failure_trackers.get(device_id)
    
    def clear_failure_tracker(self, device_id: str):
        """清除设备的失败跟踪器"""
        if device_id in self.failure_trackers:
            del self.failure_trackers[device_id]
    
    def get_monitoring_stats(self) -> Dict:
        """获取监控统计信息"""
        total_devices = len(self.failure_trackers)
        failing_devices = sum(1 for t in self.failure_trackers.values() if t.consecutive_failures > 0)
        
        return {
            'total_tracked_devices': total_devices,
            'failing_devices': failing_devices,
            'healthy_devices': total_devices - failing_devices,
            'quick_check_timeout': self.quick_check_timeout,
            'offline_threshold_seconds': self.offline_threshold_seconds,
            'max_quick_checks': self.max_quick_checks
        }
    
    def __del__(self):
        """清理资源"""
        if hasattr(self, 'executor'):
            self.executor.shutdown(wait=False)
